package com.mall.auth.controller;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.mall.auth.entity.OAuth2Provider;
import com.mall.auth.service.OAuth2Service;
import com.mall.common.result.Result;
import com.mall.common.util.RedisUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 二维码登录控制器
 */
@Tag(name = "二维码登录接口")
@Slf4j
@RestController
@RequestMapping("/auth/qr")
public class QRCodeController {

    @Autowired
    private OAuth2Service oAuth2Service;
    
    @Autowired
    private RedisUtil redisUtil;

    @Operation(summary = "生成二维码")
    @GetMapping("/generate/{provider}")
    public ResponseEntity<byte[]> generateQRCode(
            @PathVariable("provider") String provider,
            @RequestParam(value = "width", defaultValue = "200") int width,
            @RequestParam(value = "height", defaultValue = "200") int height) {
        try {
            OAuth2Provider providerEnum = OAuth2Provider.fromCode(provider);
            
            // 生成状态码，用于防止CSRF攻击和标识二维码
            String state = UUID.randomUUID().toString();
            
            // 生成二维码内容
            String qrContent = oAuth2Service.generateQRCodeContent(providerEnum, state);
            
            // 将state存入Redis，设置5分钟过期时间
            // 存储二维码信息，包括提供商和创建时间
            Map<String, Object> qrInfo = new HashMap<>();
            qrInfo.put("provider", provider);
            qrInfo.put("createTime", System.currentTimeMillis());
            qrInfo.put("status", "pending"); // pending, scanned, confirmed, expired, failed
            // 添加访问频率限制信息
            qrInfo.put("accessCount", 0);
            qrInfo.put("lastAccessTime", System.currentTimeMillis());
            redisUtil.set("qr:info:" + state, qrInfo, 300);
            
            // 生成二维码图片
            QRCodeWriter qrCodeWriter = new QRCodeWriter();
            Map<EncodeHintType, Object> hints = new HashMap<>();
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
            
            BitMatrix bitMatrix = qrCodeWriter.encode(qrContent, BarcodeFormat.QR_CODE, width, height, hints);
            
            ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
            MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream);
            
            byte[] pngData = pngOutputStream.toByteArray();
            
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.IMAGE_PNG);
            
            // 在响应头中返回state，用于后续查询扫码状态
            headers.add("QR-State", state);
            
            return new ResponseEntity<>(pngData, headers, HttpStatus.OK);
        } catch (Exception e) {
            log.error("生成二维码异常: ", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }

    @Operation(summary = "检查扫码状态")
    @GetMapping("/check/{state}")
    public Result<Map<String, Object>> checkScanStatus(@PathVariable("state") String state) {
        try {
            // 检查访问频率，防止恶意请求
            if (!checkAccessFrequency(state)) {
                Map<String, Object> rateLimitResult = new HashMap<>();
                rateLimitResult.put("status", "failed");
                rateLimitResult.put("message", "请求过于频繁，请稍后再试");
                return Result.failed("请求过于频繁，请稍后再试");
            }
            
            // 从Redis中获取扫码二维码信息
            Object qrInfoObj = redisUtil.get("qr:info:" + state);
            
            Map<String, Object> result = new HashMap<>();
            
            if (qrInfoObj != null) {
                Map<String, Object> qrInfo = (Map<String, Object>) qrInfoObj;
                String status = (String) qrInfo.get("status");
                
                result.put("status", status);
                
                switch (status) {
                    case "pending":
                        result.put("message", "等待扫码");
                        break;
                    case "scanned":
                        result.put("message", "已扫码，等待确认");
                        break;
                    case "confirmed":
                        result.put("message", "已确认");
                        // 获取用户信息
                        Object userInfo = redisUtil.get("qr:user:" + state);
                        if (userInfo != null) {
                            result.put("userInfo", userInfo);
                        }
                        // 删除已处理的状态（保留一段时间供客户端查询结果）
                        redisUtil.expire("qr:info:" + state, 60); // 1分钟后过期
                        redisUtil.expire("qr:user:" + state, 60); // 1分钟后过期
                        break;
                    case "expired":
                        result.put("message", "二维码已过期");
                        break;
                    case "failed":
                        result.put("message", "登录失败");
                        Object errorMsg = redisUtil.get("qr:error:" + state);
                        if (errorMsg != null) {
                            result.put("error", errorMsg);
                        }
                        break;
                    case "cancelled":
                        result.put("message", "用户取消授权");
                        break;
                }
            } else {
                result.put("status", "expired");
                result.put("message", "二维码已过期");
            }
            
            return Result.success(result);
        } catch (Exception e) {
            log.error("检查扫码状态异常: ", e);
            return Result.failed("检查扫码状态失败: " + e.getMessage());
        }
    }
    
    /**
     * 检查访问频率，防止恶意请求
     * 
     * @param state 二维码状态标识
     * @return 是否允许访问
     */
    private boolean checkAccessFrequency(String state) {
        try {
            Object qrInfoObj = redisUtil.get("qr:info:" + state);
            if (qrInfoObj != null) {
                Map<String, Object> qrInfo = (Map<String, Object>) qrInfoObj;
                Integer accessCount = (Integer) qrInfo.getOrDefault("accessCount", 0);
                Long lastAccessTime = (Long) qrInfo.getOrDefault("lastAccessTime", System.currentTimeMillis());
                
                // 限制每秒最多访问10次
                if (System.currentTimeMillis() - lastAccessTime < 1000 && accessCount >= 10) {
                    return false;
                }
                
                // 更新访问计数和时间
                if (System.currentTimeMillis() - lastAccessTime >= 1000) {
                    accessCount = 0; // 重置计数
                }
                accessCount++;
                
                qrInfo.put("accessCount", accessCount);
                qrInfo.put("lastAccessTime", System.currentTimeMillis());
                redisUtil.set("qr:info:" + state, qrInfo, 300);
                
                return true;
            }
        } catch (Exception e) {
            log.warn("检查访问频率异常: ", e);
        }
        return true; // 出现异常时默认允许访问
    }
}